<template>
  <div class="chat">
    <el-scrollbar class="rbp_content" ref="rbp_contentRef">
      <template v-if="msgList.length === 0">
        <div class="rbp_posit">
          <div class="rbpc_img"></div>
          <div class="rbpc_text">
            你好，我是财小智
            <br />
            有什么问题可以帮到您？
          </div>
        </div>
      </template>
      <template v-else>
        <div class="rbp_dialogue" ref="rbp_dialogueRef">
          <div class="rbpd_msg" v-for="item in msgList" :key="item.id" :id="`message${item.id}`">
            <div :class="item.type == 2 ? 'rbpd_box meSend' : 'rbpd_box'">
              <div :class="item.type == 2 ? 'rbpdm_img rbpdm_imgMe' : 'rbpdm_img'"></div>
              <div :class="item.type == 2 ? 'rbpdm_txt rbpdm_txtMe' : 'rbpdm_txt'">
                {{ renderMessageFn(item.msg) }}
                <i class="flicker-box" v-if="item.type === 1 && item.isShowFlicker"></i>
              </div>
              <div class="rbpdm_right" v-if="item.isAsk">
                <div class="rbpdm_player operating" v-if="!item.isPlay" @click="handleClickPaly(item.id)"></div>
                <div class="rbpdm_pause operating" v-else @click="handleClickTPaly(item.id)"></div>
              </div>
            </div>
            <div :class="{ 'rbmsg_btm': true, 'help': item.feedbackResult === 1, 'notHelp': item.feedbackResult === 0 }"
              v-if="item.isFinish">
              <div class="helpful" @click="handleClickHelp(item.id)">
                <img :src="(getHelpIcon(item.feedbackResult))" alt="">
                <span>有帮助</span>
              </div>
              <div class="helpLine"></div>
              <div class="notHelpful" @click="handleClickNotHelp(item.id)">
                <img :src="(getNotHelpIcon(item.feedbackResult))" alt="">
                <span>无帮助</span>
              </div>
            </div>
          </div>
        </div>
      </template>
    </el-scrollbar>
    <!-- 搜索 -->
    <div class="rbp_serch">
      <input class="uni-input" placeholder="请输入您要提问的财税问题" v-model="questionText" @focus="handleInputFocus"
        :disabled="inputDisable" />
      <div class="uni-seach" @click="handleClickSearch"></div>
    </div>
    <div class="rbp_radio">
      <div class="rbpr_group">
        <div class="rbprg_left" v-if="audioImage"></div>
        <div class="rbprg_middle" @click="handleStartAudio"></div>
        <div class="rbprg_right" v-if="audioImage"></div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, nextTick, watch, inject, onActivated, onDeactivated, onMounted, onBeforeUnmount } from 'vue'
import WebSocketWithHeartbeat from '@/utils/scoket.js'
import { useStore } from 'vuex'
import { help, thirdMark } from '@/apis/chat'

const screenFn = inject('screenFn')

const store = useStore()

watch(() => store.state.isScreen, () => {
  msgList.value = []
  inputDisable.value = false
  // window.android.stopTTS()
  if(ws){
    ws.close()
  }
})

const renderMessageFn = (message) => {
  // message 只有两种状态, 字符串和数组
  if (typeof message === 'string') {
    return message
  } else {
    return message.join('')
  }
}

// 消息id
let id = 0
// 消息列表
const msgList = ref([])

const rbp_contentRef = ref()
const rbp_dialogueRef = ref()
function chartScrollBottom() {
  rbp_contentRef.value.setScrollTop(rbp_dialogueRef.value.offsetHeight)
}

// scoket实例
let ws = null
onMounted(() => {
  ws = new WebSocketWithHeartbeat(getMessage)
  ws.connect()
  // window.android.getCallback(true)
  // window.android.stopTTS()
})

onActivated(() => {
  if (msgList.value.length !== 0) {
    chartScrollBottom()
  }
  // 获取语音的回调
  window.getAudioMessage = getAudioMessage
  // 当语音播放完成时的回调
  window.onStopPlay = onStopPlay
  window.activeAudio = activeAudio
})

onDeactivated(() => {
  // 切换到其他页面被缓存时，如果有正在播放的语音，则停止掉
  if (currentPlayMessageId) {
    handleClickTPaly(currentPlayMessageId)
  }
})

// 输入框问题
const questionText = ref('')

// 输入框获取焦点时
const handleInputFocus = () => {
  screenFn()
  // 输入框获取到焦点时，判断消息列表是否为空
  if (msgList.value.length === 0) {
    // 如果消息列表为空，则初始化一条记录
    msgList.value = [{
      id: 0,
      type: 1,
      isAsk: 0,
      msg: '你好，我是财小智，有什么问题可以帮到您？'
    }]
  }
}


let responseTimer = null
// 发送问题
const handleClickSearch = () => {
  screenFn()
  if (!questionText.value || inputDisable.value) return
  inputDisable.value = true
  msgList.value.push({
    id: ++id,
    type: 2,
    isAsk: 0,
    msg: questionText.value
  })
  ws.send({
    prompt: questionText.value
  })
  questionText.value = ''
  nextTick(() => {
    chartScrollBottom()
  }).then(() => {
    // 向消息列表中 push 一条空白回复
    msgList.value.push({
      id: ++id,
      type: 1,
      isAsk: 0,
      isFinish: false,
      isShowFlicker: true,
      msg: []
    })
    nextTick(() => {
      chartScrollBottom()
    })
  })

  // 手动发送消息时，判断当前有没有播放的语音，如果有，则停止播放
  if (currentPlayMessageId) {
    handleClickTPaly(currentPlayMessageId)
  }
  sendDefaultContent()
}

// 发送消息后（手动发送或者接收语音发送）开启一个定时器，如果8秒内，gtp没有回应，则回复一个默认内容
const sendDefaultContent = () => {
  // 开启一个延时器，
  responseTimer = window.setTimeout(() => {
    ws.close()
    // 找出当前 id 回复消息对象
    const currentMessage = msgList.value.find(item => item.id === id)
    if(!currentMessage) return
    currentMessage.isShowFlicker = false
    currentMessage.msg = '抱歉~我没有完全理解您的问题，您可以尝试其他提问方式。或者您还有其他问题我可以帮助解答吗？'
    nextTick(() => {
      chartScrollBottom()
    })
    inputDisable.value = false
    responseTimer = null
  }, 8000)
}


// 本条消息回复的所有内容
let responseContent = ''
// 替换 responseContent 中的小有为小智
function formatResponseContent(content){
  return content.replace(/小有/g, "小智")
}
// 接受消息
const getMessage = (e) => {
  // 如果gtp有回应，则清除调延时器
  window.clearTimeout(responseTimer)
  screenFn()
  const data = JSON.parse(e)
  // 找出当前 id 的消息对象
  const currentMessage = msgList.value.find(item => item.id === id)
  // 判断消息类型
  if (data.type === 'add') {
    responseContent += data.content
    // 有 gpt 回复内容
    currentMessage.feedbackResult = null
  } else if (data.type === 'finish') {
    // 回复结束
    currentMessage.requestId = data.requestId
    // 将内容中的小有替换成小智
    responseContent = formatResponseContent(responseContent)
    renderResponseContent()
  }
}

// 加载 gtp 回复文字的函数
let renderResponseTimer = null
// 计数器
// 默认从 0 开始
let index = 0
const renderResponseContent = () => {
  screenFn()
  // 找出当前 id 的消息对象
  const currentMessage = msgList.value.find(item => item.id === id)
  if(!currentMessage){
    // 如果找不到
    cancelAnimationFrame(renderResponseTimer)
    inputDisable.value = false
    responseContent = ''
    index = 0
    return
  }
  // 判断计数器
  if (index >= responseContent.length) {
    cancelAnimationFrame(renderResponseTimer)
    renderResponseTimer = null
    currentMessage.isFinish = true
    currentMessage.isShowFlicker = false
    inputDisable.value = false
    responseContent = ''
    index = 0
    nextTick(() => {
      chartScrollBottom()
    })
    return
  }
  currentMessage.msg.push(responseContent[index])
  index += 1
  nextTick(() => {
    chartScrollBottom()
    renderResponseTimer = requestAnimationFrame(renderResponseContent)
  })
  // 播放语音
  // 判断当前需要播放的文字
  if (responseContent.length > 15) {
    if(index === 15){
      // 如果当前文字长度大于 15，则从第 15 个字开始播放
      currentMessage.isAsk = 1
      currentMessage.isPlay = true
      // window.android.playText(responseContent)
      currentPlayMessageId = id
    }
  } else {
    // 如果当前文字长度小于或等于 20，则立即播放
    currentMessage.isAsk = 1
    currentMessage.isPlay = true
    // window.android.playText(responseContent)
    currentPlayMessageId = id
  }
}

// 点击无帮助
const handleClickNotHelp = (id) => {
  screenFn()
  handleClickTPaly(id)
  // 找到当前消息项
  const currentMessage = msgList.value.find(item => item.id === id)
  // 判断当前的消息是否有反馈结果
  if (currentMessage && currentMessage.feedbackResult === null) {
    // 如果存在消息以及消息的反馈结果是null
    currentMessage.feedbackResult = 0 // 0 为无帮助
    const currentMessageIndex = msgList.value.findIndex(item => item.id === id)
    // 往 msgList 列表中 currentMessageIndex 位置处插入一条消息
    msgList.value.splice(currentMessageIndex + 1, 0, {
      id: ++id,
      type: 1,
      isAsk: 0,
      isFinish: false,
      msg: '抱歉~财小智将持续进行系统学习希望下次可以为您解答(*AV^)。或者您可以尝试其他提问方式。'
    })
    nextTick(() => {
      chartScrollBottom()
    })
    const lastMessage = msgList.value[currentMessageIndex - 1]
    help({
      type: 0,
      info: renderMessageFn(currentMessage.msg),
      issue: lastMessage.msg
    })
  }
}
// 有帮助的图标
const getHelpIcon = (feedbackResult) => {
  if (feedbackResult === null) {
    // 无反馈时
    return require('@/assets/image/help.png')
  } else if (feedbackResult === 0) {
    // 反馈无帮助
    return require('@/assets/image/help-none.png')
  } else if (feedbackResult === 1) {
    // 反馈无帮助
    return require('@/assets/image/help-s.png')
  }
}
// 点击有帮助
const handleClickHelp = (id) => {
  screenFn()
  // 找到当前消息项
  const currentMessage = msgList.value.find(item => item.id === id)
  // 判断当前的消息是否有反馈结果
  if (currentMessage && currentMessage.feedbackResult === null) {
    // 如果存在消息以及消息的反馈结果是null
    currentMessage.feedbackResult = 1 // 1 为有帮助
    const currentMessageIndex = msgList.value.findIndex(item => item.id === id)
    const lastMessage = msgList.value[currentMessageIndex - 1]
    help({
      type: 1,
      info: renderMessageFn(currentMessage.msg),
      issue: lastMessage.msg
    })
    // 如果存在 requestId， 则调用云帐房标记接口
    if(currentMessage.requestId){
      thirdMark(currentMessage.requestId)
    }
  }
}
// 无帮助的图标
const getNotHelpIcon = (feedbackResult) => {
  if (feedbackResult === null) {
    // 无反馈时
    return require('@/assets/image/notHelp.png')
  } else if (feedbackResult === 0) {
    // 反馈无帮助
    return require('@/assets/image/notHelp-s.png')
  } else if (feedbackResult === 1) {
    // 反馈无帮助
    return require('@/assets/image/notHelp-none.png')
  }
}

// 输入框禁用状态
const inputDisable = ref(false)

// 当前播放消息的id
let currentPlayMessageId = null
// 点击播放
const handleClickPaly = (id) => {
  screenFn()
  currentPlayMessageId = id
  const currentMessage = msgList.value.find(item => item.id === id)
  // 将当前消息的图标切换到暂停状态
  currentMessage.isPlay = true
  // 将其他消息的图标切换到播放状态
  msgList.value.forEach(item => {
    if (item.id !== id && item.isPlay) {
      item.isPlay = false
    }
  })
  // 判断当前消息是否显示完毕
  if (currentMessage.isFinish) {
    // 如果显示完毕，则播放当前消息的内容
    // window.android.playText(renderMessageFn(currentMessage.msg))
  } else {
    // 如果没有显示完毕，则播放当前内存中的文字
    // window.android.playText(responseContent)
  }
}

// 自动播放停止后的回调
const onStopPlay = () => {
  screenFn()
  // 如果当前播放的语音id为 null 时，要 return 出去
  if (!currentPlayMessageId) return
  const currentMessage = msgList.value.find(item => item.id === currentPlayMessageId)
  currentMessage.isPlay = false
  currentPlayMessageId = null
}

// 点击暂停
const handleClickTPaly = (id) => {
  screenFn()
  const currentMessage = msgList.value.find(item => item.id === id)
  if (currentMessage) {
    currentMessage.isPlay = false
    // window.android.stopTTS()
    currentPlayMessageId = null
  }
}


// 本次手动唤起语音所说的话
let audioMessage = ''

// 音频声浪显示隐藏
const audioImage = ref(false)
// 点击音频按钮后，如果安卓端没有触发回调，则自己手动5秒后关闭音频
let handleStartAudioTimer = null
// 点击音频按钮
const handleStartAudio = () => {
  screenFn()
  // 点击音频时判断
  if (inputDisable.value) return
  // 点击音频按钮，如果不是处于禁用状态，如果有正在播放的语音，则停止掉
  if (currentPlayMessageId) {
    handleClickTPaly(currentPlayMessageId)
  }
  audioImage.value = true
  audioMessage = ''
  if (handleStartAudioTimer) {
    window.clearTimeout(handleStartAudioTimer)
  }
  handleStartAudioTimer = window.setTimeout(() => {
    // 如果5秒内安卓端没有调用回调函数，则手动关闭音频
    handleEndAudio()
  }, 5000)
  // window.android.getCallback(true)
}

// 音频结束
const handleEndAudio = () => {
  screenFn()
  audioImage.value = false
  audioTimer = null
  audioMessage = ''
  // window.android.stopTTS()
}


// 语音定时器id
let audioTimer = null
// 获取音频方法（用户点击按钮唤起的）
const getAudioMessage = (e) => {
  screenFn()
  // 主动唤起时也要显示音浪图片
  audioImage.value = true
  // 如果安卓端触发了回调，则清除调上面的延时器
  window.clearTimeout(handleStartAudioTimer)
  window.clearTimeout(activeAudioTimer)
  if (audioTimer) {
    window.clearTimeout(audioTimer)
  }
  audioMessage = audioMessage + e
  audioTimer = setTimeout(() => {
    if (inputDisable.value) {
      // 如果输入框处于禁用状态，则将再次提问的文字显示到输入框内，等本地问题回应完后，用户手动点击发送
      questionText.value = audioMessage
      audioImage.value = false
      audioMessage = ''
      return
    }
    // 如果 audioMessage 是空字符或者是undefind，则 return出去
    if(!audioMessage.trim()){
      audioImage.value = false
      audioMessage = ''
      return
    }
    inputDisable.value = true
    msgList.value.push({
      id: ++id,
      type: 2,
      isAsk: 0,
      msg: audioMessage
    })
    ws.send({
      prompt: audioMessage
    })
    questionText.value = ''
    handleEndAudio()
    nextTick(() => {
      chartScrollBottom()
    }).then(() => {
      // 向消息列表中 push 一条空白回复
      msgList.value.push({
        id: ++id,
        type: 1,
        isAsk: 0,
        isFinish: false,
        isShowFlicker: true,
        msg: []
      })
      nextTick(() => {
        chartScrollBottom()
      })
    })
    sendDefaultContent()
  }, 2000)
}

let activeAudioTimer = null
// 主动唤起财小智时的回调
const activeAudio = () => {
  audioImage.value = true
  activeAudioTimer = window.setTimeout(() => {
    audioImage.value = false
  }, 3000)
}
</script>

<style scoped lang="less">
.chat {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;

  .rbp_content {
    flex: 1;
    width: 100%;
    display: flex;
    justify-content: center;

    /deep/.el-scrollbar__wrap {
      width: 100%;
    }

    .rbp_posit {
      display: flex;
      align-items: center;
      justify-content: center;
      flex: 1;
      margin-top: .5208rem;

      .rbpc_img {
        width: 1.0573rem;
        height: 1.5104rem;
        background-image: url('@/assets/image/robot.png');
        background-size: 100% 100%;
        animation: rbpc_img 1s infinite alternate;
        margin-right: .5938rem;
      }

      @keyframes rbpc_img {
        0% {
          transform: translateY(-10px);
        }

        100% {
          transform: translateY(10px);
        }
      }

      .rbpc_text {
        font-size: .3125rem;
        font-family: 'AlibabaPuHuiTi';
        font-weight: 400;
        line-height: .625rem;
      }
    }

    // 消息
    .rbp_dialogue {
      width: 100%;
      display: flex;
      flex-direction: column;
      row-gap: .1302rem;

      .rbpd_msg {
        display: flex;
        width: 100%;
        flex-direction: column;
        column-gap: .0781rem;

        .meSend {
          flex-direction: row-reverse;

          .rbpdm_imgMe {
            background-image: url('@/assets/image/me.png') !important;
            background-size: contain !important;
            background-repeat: no-repeat;
          }

          .rbpdm_txtMe::before {
            left: auto !important;
            right: -0.1042rem !important;
            border-top-color: transparent !important;
            border-right-color: transparent !important;
            border-bottom-color: transparent !important;
            border-left-color: #fff !important;
          }
        }

        .rbpd_box {
          display: flex;
          width: 100%;
          column-gap: .0781rem;

          .rbpdm_img {
            width: .4063rem;
            height: .3802rem;
            background-image: url('@/assets/image/rabbit.png');
            background-size: 100% 100%;
            background-repeat: no-repeat;
          }

          .rbpdm_txt {
            max-width: 65%;
            line-height: .2083rem;
            margin-top: .0521rem;
            height: auto;
            background-color: #fff;
            border-radius: .0313rem;
            color: #000;
            border: 1px solid #fff;
            padding: .0521rem;
            box-sizing: border-box;
            position: relative;
            font-size: .1563rem;
            font-family: 'AlibabaPuHuiTi';
            word-break: break-all;

            .flicker-box {
              display: inline-block;
              vertical-align: middle;
              width: .026rem;
              height: .2083rem;
              background-color: #000;
              animation: flicker .8s infinite;
            }

            @keyframes flicker {
              0% {
                opacity: 0;
              }

              50% {
                opacity: 1;
              }

              100% {
                opacity: 0;
              }
            }
          }

          .rbpdm_txt::before {
            position: absolute;
            top: .1042rem;
            left: -0.1042rem;
            content: '';
            border-width: .0521rem;
            border-style: solid;
            border-top-color: transparent;
            border-right-color: #fff;
            border-bottom-color: transparent;
            border-left-color: transparent;
          }

          .rbpdm_right {
            width: .2604rem;
            display: flex;
            flex-direction: column;
            column-gap: .0781rem;
            margin-top: .0521rem;

            .operating {
              width: .3646rem;
              height: .3646rem;
              background-size: 100% 100%;
            }

            .rbpdm_player {
              background-image: url('@/assets/image/player.png');
            }

            .rbpdm_pause {
              background-image: url('@/assets/image/pause.png');
            }
          }
        }

        .rbmsg_btm {
          display: flex;
          align-items: center;
          border-radius: .1823rem;
          font-size: .1563rem;
          background: linear-gradient(to bottom, #F3F7FF 0%, #cbdffd 100%);
          width: fit-content;
          padding: .0521rem .1042rem;
          margin-left: .4688rem;
          margin-top: .0521rem;
          font-size: .1563rem;
          font-family: 'AlibabaPuHuiTi';
          color: #287de3;

          &.help {
            .helpful {
              color: #ef6b3b;
            }

            .notHelpful {
              color: #999999;
            }
          }

          &.notHelp {
            .helpful {
              color: #999999;
            }

            .notHelpful {
              color: #ef6b3b;
            }
          }

          img {
            width: .1719rem;
            height: .1823rem;
          }

          .helpLine {
            width: .0156rem;
            height: .2031rem;
            background-color: #0d4fc7;
            border-radius: 2px;
            margin: 0 .1302rem;
          }
        }
      }
    }
  }

  .rbp_serch {
    width: 90%;
    height: .5833rem;
    padding: 0 .1094rem 0 .2604rem;
    background-image: url('@/assets/image/search.png');
    background-size: 100% 100%;
    background-repeat: no-repeat;
    display: flex;
    align-items: center;
    margin: .1563rem 0;
    border-radius: .2604rem;
    overflow: hidden;

    .uni-input {
      flex: 1;
      font-size: .1563rem;
      color: #333;
    }

    .uni-seach {
      width: .3906rem;
      height: .3906rem;
      background-image: url('@/assets/image/searchBtn.png');
      background-size: contain;
      background-repeat: no-repeat;
      cursor: pointer;
    }
  }

  .rbp_radio {
    display: flex;
    justify-content: center;

    .rbpr_group {
      display: flex;
      align-items: center;
      gap: .0781rem;

      .rbprg_left,
      .rbprg_right {
        width: .4583rem;
        height: .224rem;
        background-image: url('@/assets/image/talking.webp');
        background-size: 100% 100%;
        background-repeat: no-repeat;
      }

      .rbprg_middle {
        width: .6875rem;
        height: .6875rem;
        background-image: url('@/assets/image/microphone.png');
        background-size: 100% 100%;
        background-repeat: no-repeat;
      }
    }
  }
}</style>
